package com.infolab.aims.web;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.JExcelApiExporter;
import net.sf.jasperreports.engine.export.JRXlsExporterParameter;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.infolab.aims.GlobalConstant;
import com.infolab.aims.common.AbstractController;
import com.infolab.aims.common.CommonUtil;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;
import com.infolab.aims.common.exception.ResourceNotFoundException;
import com.infolab.aims.domain.Department;
import com.infolab.aims.domain.asset.AssetBalance;
import com.infolab.aims.service.intf.DepartmentService;


@Controller
@RequestMapping("/department")
@SessionAttributes(value=DepartmentController.SESSION_SEARCH_CRITERIA)
public class DepartmentController extends AbstractController {
	
	public static final String SESSION_SEARCH_CRITERIA ="searchCriteria";
	public static final String CONTEXT_PATH ="/department";
	public static final String JSP_MAIN_FORM="departmentForm";
	public static final String JSP_MAIN_SEARCH="departmentSearch";
	
	Log logger = LogFactory.getLog(getClass());

	@Autowired
	DepartmentService departmentService;

	@RequestMapping(value="create", method = RequestMethod.GET)
	public String createDepartment(Model model) {
		
		model.addAttribute(new Department());
		return JSP_MAIN_FORM;
	}
	
	@RequestMapping(value="create",method=RequestMethod.POST)
	public String saveDepartment(@Valid @ModelAttribute Department department, BindingResult result,Model model,HttpServletRequest request) {

		
		
		if (validateDepartments(department, true, result)) {	
			return JSP_MAIN_FORM;
		}
		
		department.setCreatedBy(getCurrentUserName(request));
		
		departmentService.createDepartment(department);
		
		setSuccessMessage(getMessageResources("success.create","department " + department.getDepartmentName()));
		
		refreshGlobalDepartment(request);
		
		return "redirect:"+CONTEXT_PATH+"/update/" + department.getDepartmentId();
		

	}
	
	@RequestMapping(value="update/{id}",method=RequestMethod.POST)
	public String updateUser(@PathVariable Integer id,@Valid @ModelAttribute Department department, BindingResult result,Model model,HttpServletRequest request) {

		if (validateDepartments(department, false, result)) {	
			return JSP_MAIN_FORM;
		}
		
		department.setModifiedBy(getCurrentUserName(request));
		
		departmentService.updateDepartment(department);
		
		setSuccessMessage(getMessageResources("success.update","department " + department.getDepartmentName()));
		
		refreshGlobalDepartment(request);
		
		return "redirect:"+CONTEXT_PATH+"/update/" + department.getDepartmentId();	
		
	}
	
	@RequestMapping(value={"update/{id}","detail/{id}"}, method = RequestMethod.GET)
	public String detailUser(@PathVariable Integer id,Model model,HttpServletRequest request) {
		
		Department department = departmentService.getDepartment(id);
		if(department==null) {
			throw new ResourceNotFoundException("Department " + id);
		}
		
		model.addAttribute(department);
		return JSP_MAIN_FORM;

	}
	

	
	@ModelAttribute(DepartmentController.SESSION_SEARCH_CRITERIA)
	public SearchCriteria getDepartmentSearchCriteria()
	{
		return new SearchCriteria();
	}
	

	@RequestMapping(value="search")
	public String searchDepartment(@ModelAttribute(DepartmentController.SESSION_SEARCH_CRITERIA) SearchCriteria searchCriteria,@ModelAttribute("currentSearchCriteria") SearchCriteria currentSearchCriteria,Model model) {
		
		if(currentSearchCriteria.getCurPage()!=null)
			searchCriteria.setCurPage(currentSearchCriteria.getCurPage());
		
		searchCriteria.addSearchCriteria("active", true);
		
		SearchResult searchResult = departmentService.searchDepartment(searchCriteria);
		model.addAttribute(searchResult);
		
		return JSP_MAIN_SEARCH;
	}
	
	@RequestMapping(value={"delete/{id}"}, method = RequestMethod.GET)
	public String deleteDepartment(@PathVariable Integer id,Model model,HttpServletRequest request) {
		
		
		Department department = departmentService.getDepartment(id);
		department.setModifiedBy(getCurrentUserName(request));
		departmentService.deleteDepartment(department);
		
		refreshGlobalDepartment(request);
		
		return "redirect:"+CONTEXT_PATH+"/search";
	}
	
	@RequestMapping(value={"importCompany"}, method = RequestMethod.POST)
	public @ResponseBody String importCompany(Model model,HttpServletRequest request,HttpServletResponse response) {

		HttpHeaders responseHeaders = new HttpHeaders();
		responseHeaders.add("Content-Type", "text/html");
		
		try
		{
			String filename = request.getHeader("X-File-Name");
			boolean flag = true;
			StringBuilder sb = new StringBuilder();
			
			MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
			Iterator<String> fileNames = multipartRequest.getFileNames();
			while(fileNames.hasNext())
			{
				filename = fileNames.next();
			}
			MultipartFile multipartFile = multipartRequest.getFile(filename);

			InputStream is = multipartFile.getInputStream();

			Workbook workbook = null;
			try
			{
				WorkbookSettings ws = new WorkbookSettings();
				ws.setInitialFileSize(1);
				workbook = Workbook.getWorkbook(is);
				Sheet sheet  = workbook.getSheet(0);

				List<Department> departments = new ArrayList<Department>();
				Map<String, Integer> deptsMap = new HashMap<String, Integer>();
				sb.append("<ul>");
				
				for(int i=1;i<sheet.getRows();i++){
					boolean inner = true;
					Department dept = new Department();
					dept.setCreatedBy(getCurrentUserName(request));
					dept.setModifiedBy(getCurrentUserName(request));
					dept.setDateCreated(new Date());
					dept.setDateModified(new Date());
					dept.setActive(true);
					String companyName = sheet.getCell(0, i).getContents();
					String companyCode = sheet.getCell(1, i).getContents();
					String contact = sheet.getCell(2, i).getContents();	
					String unitNo = sheet.getCell(3, i).getContents();
					dept.setDepartmentName(StringUtils.trim(companyName));
					dept.setDepartmentCode(StringUtils.trim(companyCode));
					dept.setDepartmentContact(StringUtils.trim(contact));
					dept.setDepartmentUnitNo(StringUtils.trim(unitNo));
					List<String> message = new ArrayList<String>();
					message.add("<li>line ["+i+"] ");
					if(StringUtils.isEmpty(companyName)){
						flag = false;
						inner = false;
						message.add("Company Name is empty, ");
					}
					if(StringUtils.isEmpty(contact)){
						flag = false;
						inner = false;
						message.add("Company Contact is empty, ");
					}
					if(StringUtils.isEmpty(unitNo)){
						flag = false;
						inner = false;
						message.add("Company Unit No is empty.");
					}
					message.add("</li> ");
					if(!inner){
						sb.append(StringUtils.join(message.iterator(), ""));
					}
					
					if(deptsMap.get(dept.getDepartmentUnitNo()) != null){
						deptsMap.put(dept.getDepartmentUnitNo(), deptsMap.get(dept.getDepartmentUnitNo()) + 1);
					}else{
						deptsMap.put(dept.getDepartmentUnitNo(), 1);
					}
					
					departments.add(dept);
				}
				sb.append("</ul>");
				
				if(!vidateImportList(departments, deptsMap, sb)){
					flag = false;
				}
				
				if(flag){
					for(Department dept : departments){
						departmentService.createDepartment(dept);
					}
				}
				
			}catch(Exception e)
			{	
				flag = false;
				sb.append("<ul><li>import company failed. Please check your excel file.</li></ul>");
				logger.error(CommonUtil.getStackTrace(e));
			}finally{
				if(workbook!=null)
				{
					workbook.close();
				}
			}
			
			Map map = new HashMap();
			map.put("success", "true");

			if(flag){
				map.put("error", "false");
				map.put("result", "<ul><li>import company successfully.</li></ul>");
			}else{
				map.put("error", "true");
				map.put("result", sb.toString());
			}

			StringWriter writer = new StringWriter();
	        ObjectMapper mapper = new ObjectMapper();
	        JsonGenerator jsonGenerator;
			try {
				jsonGenerator = mapper.getJsonFactory().createJsonGenerator(writer);
				mapper.writeValue(jsonGenerator, map);
				return writer.toString();
			} catch (IOException e) {
				logger.error(CommonUtil.getStackTrace(e));
			}
	
		} catch (Exception e) {
			
			logger.error(CommonUtil.getStackTrace(e));
		}
		
		return "[]";
	}
	
	
	@RequestMapping(value={"exportCompany"}, method = RequestMethod.GET)
	public void printAssetBalance(HttpServletRequest request,HttpServletResponse response) throws IOException {
		try
		{
			List<Department> list = (List<Department> )request.getSession().getServletContext().getAttribute(GlobalConstant.GLOBAL_DEPARTMENT);
	
			ServletOutputStream servletOutputStream = response.getOutputStream();	
			InputStream reportStream = null;
			
			reportStream =getClass().getClassLoader().getResourceAsStream("report/Company.jasper");	
			JasperPrint jasperPrint = JasperFillManager.fillReport(reportStream, null, new JRBeanCollectionDataSource(list));
			JExcelApiExporter exporterXLS = new JExcelApiExporter();
			exporterXLS.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
			exporterXLS.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
			exporterXLS.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
			exporterXLS.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
			exporterXLS.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, servletOutputStream );
			response.setHeader("Content-Disposition", "attachment; filename=Company.xls");
			response.setContentType("application/vnd.ms-excel");
			exporterXLS.exportReport();
			servletOutputStream.flush();
			servletOutputStream.close();
		}
		catch (JRException e)
		{
			logger.error(CommonUtil.getStackTrace(e));
			// display stack trace in the browser
			StringWriter stringWriter = new StringWriter();
			PrintWriter printWriter = new PrintWriter(stringWriter);
			e.printStackTrace(printWriter);
			response.setContentType("text/plain");
			response.getOutputStream().print(stringWriter.toString());
		}

	}
	
	
	private boolean validateDepartments(Department department, boolean isInsert, BindingResult result)
	{
		SearchCriteria searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("departmentUnitNo", department.getDepartmentUnitNo());
		SearchResult existDepts = departmentService.searchDepartment(searchCriteria);
		if(isInsert){
			if(CollectionUtils.isNotEmpty(existDepts.getResult())){
				result.reject("error.exist", new String[]{"Company Unit No " + department.getDepartmentUnitNo()}, "");
			}
		}else{
			if(CollectionUtils.isNotEmpty(existDepts.getResult())){
				Department existDept = (Department)existDepts.getResult().get(0);
				if(existDept.getDepartmentId().intValue() != department.getDepartmentId().intValue()){
					result.reject("error.exist", new String[]{"Company Unit No " + department.getDepartmentUnitNo()}, "");
				}
			}
		}
		return result.hasErrors();
	}
	
	private boolean vidateImportList(List<Department> list, Map<String, Integer> maps, StringBuilder sb)
	{
		boolean flag = true;
		sb.append("<ul>");
		if(maps != null){
			for(Map.Entry<String, Integer> entry : maps.entrySet()){
				if(entry.getValue() > 1){
					flag = false;
					sb.append("<li> There are "+ entry.getValue() +" companys have the same Unit No "+entry.getKey()+" in the excel file. </li>");
				}
			}
		}
		if(list != null){
			for(int i=0; i<list.size(); i++){
				Department dept = list.get(i);
				if(!validateImportUnitNo(dept.getDepartmentUnitNo())){
					flag = false;
					sb.append("<li> line ["+(i+1)+"] the system has already haven the same Unit No ["+dept.getDepartmentUnitNo()+"] in the Company Table </li>");
				}
			}
		}
		sb.append("</ul>");
		return flag;
	}
	
	
	private boolean validateImportUnitNo(String unitNo)
	{
		SearchCriteria searchCriteria = new SearchCriteria();
		searchCriteria.addSearchCriteria("departmentUnitNo", unitNo);
		SearchResult existDepts = departmentService.searchDepartment(searchCriteria);
		if(CollectionUtils.isNotEmpty(existDepts.getResult())){
			return false;
		}
		return true;
	}
}
